package com.region.http.client.simple;

import com.region.common.adapter.logging.LoggerAdapter;
import com.region.common.adapter.logging.LoggerAdapterFactory;
import com.region.common.utils.ClassUtils;
import com.region.http.client.common.Constant;
import com.region.http.client.common.ContentType;
import com.region.http.client.common.RequestType;
import com.region.http.client.config.ClientConfig;
import com.region.http.client.config.RequestAnnotationConfig;
import com.region.http.client.config.RequestAnnotationConfig.RequestAnnotationConfigBuilder;
import com.region.http.client.model.RequestParameter;
import com.region.http.client.model.RequestParameter.RequestParameterBuilder;
import com.region.http.client.model.ResponseData;
import com.region.http.client.request.HttpExecutor;
import com.region.http.client.resolver.result.*;

import java.nio.charset.Charset;
import java.util.*;

/**
 * @author liujieyu
 * @date 2024/9/3 17:29
 * @desciption
 */
public class SimpleRequest {

    private final LoggerAdapter loggerAdapter;

    private static ClientConfig clientConfig;

    private static HttpExecutor executor = ClassUtils.newInstance(HttpExecutor.DEFAULT_HTTP_EXECUTOR_CLASS);

    private static List<ResultResolver> resolvers = new LinkedList<>();

    static {
        resolvers.add(new VoidResultResolver());
        resolvers.add(new StringResultResolver());
        resolvers.add(new BinaryResultResolver(clientConfig));
        resolvers.add(new ObjectResultResolver());
        resolvers.add(new BasicResultResolver());
        resolvers.sort(Comparator.comparingInt(e -> e.getPriority()));
    }

    private RequestAnnotationConfigBuilder requestConfigBuilder = RequestAnnotationConfig.builder();

    private RequestParameterBuilder parameterBuilder = RequestParameter.builder();

    private Map<String, String> form = new HashMap<>();

    private Map<String, String> header = new HashMap<>();

    private String requestUrl;

    public SimpleRequest(ClientConfig clientConfig) {
        this.clientConfig = clientConfig == null ? ClientConfig.builder().build() : clientConfig;
        loggerAdapter = LoggerAdapterFactory.getLogger(getClass());
    }

    public SimpleRequest(String requestUrl, RequestType requestType) {
        this.clientConfig = ClientConfig.builder().build();
        loggerAdapter = LoggerAdapterFactory.getLogger(getClass());
        setRequestUrl(requestUrl);
        setRequestType(requestType);
    }

    public void setClientConfig(ClientConfig clientConfig) {
        SimpleRequest.clientConfig = clientConfig;
    }

    private void setRequestUrl(String requestUrl) {
        this.requestUrl = requestUrl;
    }

    private void setRequestType(RequestType requestType) {
        this.requestConfigBuilder.requestType(requestType);
    }

    public SimpleRequest contentType(ContentType contentType) {
        requestConfigBuilder.contentType(contentType);
        return this;
    }

    public SimpleRequest connectTime(int connectTime) {
        requestConfigBuilder.connectTime(connectTime);
        return this;
    }

    public SimpleRequest readTime(int readTime) {
        requestConfigBuilder.readTime(readTime);
        return this;
    }

    public SimpleRequest requestTime(int requestTime) {
        requestConfigBuilder.requestTime(requestTime);
        return this;
    }

    public SimpleRequest form(String key, String value) {
        form.putIfAbsent(key, value);
        return this;
    }

    public SimpleRequest header(String key, String value) {
        header.putIfAbsent(key, value);
        return this;
    }

    public SimpleRequest body(String body) {
        parameterBuilder.body(body);
        return this;
    }

    public <T> T execute(Class<T> clazz) {
        try {
            RequestAnnotationConfig requestAnnotationConfig = requestConfigBuilder.build();
            if (requestAnnotationConfig.getConnectTime() <= 0) {
                requestAnnotationConfig.setConnectTime(Constant.DEFAULT_CONNECTION_TIME);
            }
            if (requestAnnotationConfig.getReadTime() <= 0) {
                requestAnnotationConfig.setReadTime(Constant.DEFAULT_READ_TIME);
            }
            if (requestAnnotationConfig.getRequestTime() <= 0) {
                requestAnnotationConfig.setRequestTime(Constant.DEFAULT_REQUEST_TIME);
            }
            if (requestAnnotationConfig.getContentType() == null) {
                requestAnnotationConfig.setContentType(ContentType.X_WWW_FORM_URLENCODED);
            }
            ResponseData responseData = executor.execute(requestUrl, requestAnnotationConfig, parameterBuilder
                    .headers(header)
                    .form(form)
                    .build());
            for (ResultResolver resultResolver : resolvers) {
                if (resultResolver.isResolver(clazz)) {
                    return (T) resultResolver.resolver(responseData.setType(clazz));
                }
            }
            loggerAdapter.warn("The not found result resolver.");
            loggerAdapter.warn("The response data: " + new String(responseData.getBody(), Charset.defaultCharset()));
            return null;
        } catch (Throwable throwable) {
            loggerAdapter.error("The http request execute fail.", throwable);
            return null;
        }
    }
    
    /**
     * default return type
     * @return
     */
    public String execute() {
        return execute(String.class);
    }

}
